#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h> //for struct cdev
#include <linux/fs.h>   //for struct file
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <asm/uaccess.h>


#define M88E1145_MAJOR 253
#define PHYA 0x0000 
#define PHYB 0x0001
#define PHYC 0x0002
#define PHYD 0x0003

#define cr 0x00
#define EA 0x0016
#define PHYscr 0x0010
#define ePHYscr 0x0014
#define ePHYssr 0x001B
#define LEDcr 0x0018


/*MAX MDC 8.33MHz*/
static int m88e1145_major = M88E1145_MAJOR;  
struct class * m88e1145_class; /*设备结构体指针*/  
struct cdev m88e1145_dev;

static void mdc(unsigned short data)
{
	s3c2410_gpio_setpin(S3C2410_GPF(4),data);
}

 static void mdio_write(unsigned short data)
{ 
	s3c2410_gpio_setpin(S3C2410_GPF(0),data);
}
static int mdio_read(void)
{    
	int ret; 
	ret=s3c2410_gpio_getpin(S3C2410_GPF(0));
	return ret;
}

static void write_mdc_byte(unsigned short data,int j)
{
    volatile unsigned int   i;
    
    s3c2410_gpio_cfgpin(S3C2410_GPF(0), S3C2410_GPIO_OUTPUT);
    /*data change when mdc low*/
    for(i = 0; i < j; i++)
    {
        mdc(0);
        ndelay(20);
        if(data >= 0x8000)            
            mdio_write(1);
        else
            mdio_write(0);
        ndelay(40);
        mdc(1);
        ndelay(60);

        data = data << 1;
    }
     mdc(0);
}

static void phy_write(unsigned short PHY,unsigned short reg,unsigned short data)
{ 
	   write_mdc_byte(0xffff,16);
	   write_mdc_byte(0xffff,16);
	   /*0x05 stat frame 01 and write frame 01*/
	   write_mdc_byte((0x5<<12|PHY<<7|reg<<2|0x2),16);
	   write_mdc_byte(data,16);
}

static unsigned int phy_read(unsigned short PHY,unsigned short reg)
{
	unsigned int data;
	int n;

	write_mdc_byte(0xffff,16);
	write_mdc_byte(0xffff,16);
	/*0x06 stat frame 01 and read frame 10*/
	write_mdc_byte((0x6<<12|PHY<<7|reg<<2|0x2),16);

	s3c2410_gpio_cfgpin(S3C2410_GPF(0), S3C2410_GPIO_INPUT);
	s3c2410_gpio_pullup(S3C2410_GPF(0),1);

	for(n=15;n>=0;n--)
	 {		   
		if((mdio_read()!=0))
		   data=data|0x01;
		ndelay(40);
		if(n) data<<=1;
		mdc(1);
		ndelay(60);
		mdc(0);
		ndelay(20);
	 }
	return data;
}

static void m88e1145_initaltion(void)
{
	// PHYA init  
	  phy_write(PHYA,EA,0x0000);    		  
	  phy_write(PHYA,cr,0x8000);   
	  phy_write(PHYA,EA,0x0001);    
	  phy_write(PHYA,cr,0x8000);		  
	  ndelay(1000);  // have change the delay time
	  
	  phy_write(PHYA,EA,0x0000);	       
	  phy_write(PHYA,cr,0x1140);	      
	  phy_write(PHYA,EA,0x0001);	    
	  phy_write(PHYA,cr,0x1140);     
	      
	  
	  phy_write(PHYA,0X0004,0x0060);         
	  phy_write(PHYA,ePHYscr,0x0c66);         
	  phy_write(PHYA,ePHYssr,0x008B);         

	  
	  phy_write(PHYA,PHYscr,0x68);             
	  phy_write(PHYA,LEDcr,0x4106);            
	  
	  phy_write(PHYA,EA,0x0000);               
	  phy_write(PHYA,cr,0x9140);               
	  phy_write(PHYA,EA,0x0001);              
	  phy_write(PHYA,cr,0x9140);  
	  
	// PHYB init
	  phy_write(PHYB,EA,0x0000);
	  phy_write(PHYB,cr,0x8000);
	  phy_write(PHYB,EA,0x0001);
	  phy_write(PHYB,cr,0x8000);
	  ndelay(1000);
	  
	  phy_write(PHYB,EA,0x0000);
	  phy_write(PHYB,cr,0x1140);
	  phy_write(PHYB,EA,0x0001);
	  phy_write(PHYB,cr,0x1140);
	  
	  phy_write(PHYB,0X0004,0x0060);
	  phy_write(PHYB,ePHYscr,0x0c66);
	  phy_write(PHYB,ePHYssr,0x008B);
	  
	  phy_write(PHYB,PHYscr,0x0068);
	  phy_write(PHYB,LEDcr,0x4106);
	  
	  phy_write(PHYB,EA,0x0000);
	  phy_write(PHYB,cr,0x9140);
	  phy_write(PHYB,EA,0x0001);
	  phy_write(PHYB,cr,0x9140);
	  
  	 //PHYC init
	  phy_write(PHYC,EA,0x0000);
	  phy_write(PHYC,cr,0x8000);
	  phy_write(PHYC,EA,0x0001);
	  phy_write(PHYC,cr,0x8000);
	  ndelay(1000);
	  
	  phy_write(PHYC,EA,0x0000);
	  phy_write(PHYC,cr,0x1140);
	  phy_write(PHYC,EA,0x0001);
	  phy_write(PHYC,cr,0x1140);
	  
	  phy_write(PHYC,0X0004,0x0060);
	  phy_write(PHYC,ePHYscr,0x0c66);
	  phy_write(PHYC,ePHYssr,0x008B);
	  
	  phy_write(PHYC,PHYscr,0x0068);
	  phy_write(PHYC,LEDcr,0x4106);
	  
	  phy_write(PHYC,EA,0x0000);
	  phy_write(PHYC,cr,0x9140);
	  phy_write(PHYC,EA,0x0001);
	  phy_write(PHYC,cr,0x9140);

	  
	  //PHYD init
	  phy_write(PHYD,EA,0x0000);
	  phy_write(PHYD,cr,0x8000);
	  phy_write(PHYD,EA,0x0001);
	  phy_write(PHYD,cr,0x8000);
	  ndelay(1000);
	  
	  phy_write(PHYD,EA,0x0000);
	  phy_write(PHYD,cr,0x1140);
	  phy_write(PHYD,EA,0x0001);
	  phy_write(PHYD,cr,0x1140);
	  
	  phy_write(PHYD,0X0004,0x0060);
	  phy_write(PHYD,ePHYscr,0x0c66);
	  phy_write(PHYD,ePHYssr,0x008B);
	  
	  phy_write(PHYD,PHYscr,0x0068);
	  phy_write(PHYD,LEDcr,0x4106);
	  
	  phy_write(PHYD,EA,0x0000);
	  phy_write(PHYD,cr,0x9140);
	  phy_write(PHYD,EA,0x0001);
	  phy_write(PHYD,cr,0x9140);
	  //
	  ndelay(10000);
	 
	  printk("------------%s----ok------------\n",__FUNCTION__);

}


unsigned int phy1145_addr=0;
unsigned int reg1145_addr=0;

static ssize_t m88e1145_read(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
	int n;
	int ret;
	unsigned int data=0x00;

	write_mdc_byte(0xffff,16);
	write_mdc_byte(0xffff,16);

	write_mdc_byte((0x6<<12|phy1145_addr<<7|0x01<<2|0x2),16);

	
	s3c2410_gpio_cfgpin(S3C2410_GPF(0), S3C2410_GPIO_INPUT);
	s3c2410_gpio_pullup(S3C2410_GPF(0),1);

	for(n=15;n>=0;n--)
	 {		   
		if((mdio_read()!=0))
			{
		   data=data|0x01;
			}
		ndelay(30);
		if(n) data<<=1;
		mdc_write(0);
		ndelay(40);
		mdc_write(1);
		ndelay(10); 		
	 }

	 ret=copy_to_user(user_buf,&data,5);
	if(ret<0)
		{
		printk("------------err:copy_to_user\n----------------\n");
	}

	return 0;

}
static ssize_t m88e1145_write(struct file *file,
				      const char __user *user_buf, size_t count,
				      loff_t *ppos)
{
	int ret;
	unsigned int buffer;
	unsigned int data;

	ret=copy_from_user(&buffer,user_buf,count);

	if(ret<0)
		{
		printk("------------err:copy_from_user\n----------------\n");
		}
/*
* buffer协议phy_addr 5位, reg_addr 5位，data 16位
*/
		
	  phy1145_addr	  =buffer;
//	  reg1145_addr	  =((buffer>>16)&(0x1f));

//    phy1145_addr    =(buffer&(0x1f<<21))>>21;
//    reg1145_addr    =((buffer>>16)&(0x1f));
//            data    =(buffer&0xffff);

//	printk("-buffer--%x-phy1145_addr--%x--reg1145_addr--%x--data-%x---\n",buffer,phy1145_addr,reg1145_addr,data);
	
//	phy_write(phy1145_addr,reg1145_addr,data);
	
	return 0;

}

static int m88e1145_open(struct inode *inode, struct file *filp) 
{
	m88e1145_initaltion();
	phy_write(0x0000,0x0016,0x0000);
	phy_write(0x0001,0x0016,0x0000);
	phy_write(0x0002,0x0016,0x0000);
	phy_write(0x0003,0x0016,0x0000);
	
	return 0;
}

static int m88e1145_release(struct inode *inode, struct file *filp)      
{  
    return 0;  
}  


static const struct file_operations m88e1145_fops={
	.owner=THIS_MODULE,
	.open=m88e1145_open,
	.read=m88e1145_read,
	.release=m88e1145_release,
	.write=m88e1145_write,
};


static int __init m88e1145_init(void)
{
	printk("------------%s----------------\n",__FUNCTION__);

	int ret;
	ssize_t devno;

	ret=register_chrdev(m88e1145_major,"m88e1145",&m88e1145_fops);
	if(ret<0)
		{
		printk("err:register_chrdev\n");
	}

    devno = MKDEV(m88e1145_major, 0);  

	m88e1145_class = class_create(THIS_MODULE, "m88e1145");  
    device_create(m88e1145_class, NULL,devno, NULL, "m88e1145");  
	
	printk("------------%s---OK-------------\n",__FUNCTION__);
	
	return 0;
}

static void __exit m88e1145_exit(void)
{
	printk("%s\n",__FUNCTION__);

   unregister_chrdev(m88e1145_major, "m88e1145");
   device_destroy(m88e1145_class, MKDEV(m88e1145_major,0));  
   class_destroy(m88e1145_class);


}


module_init(m88e1145_init);
module_exit(m88e1145_exit);
MODULE_DESCRIPTION("Marvell PHY_init driver");
MODULE_AUTHOR(" qifei");
MODULE_LICENSE("GPL");
